/*
 * Rhy Selector Core Lib
 *
 * @author Fabio Miranda Costa, Tristan Maindron
 * 
 */

(function(global, document){

	var elements,
		parsed,
		parsedClasses,
		parsedPseudos,
		pseudos = {},
		context,
		currentDocument,
		reTrim = /^\s+|\s+$/g;

	var supports_querySelectorAll = !!document.querySelectorAll;

	var $u = function(selector, _context, append){
		elements = append || [];
		context = _context || $u.context;
		if (supports_querySelectorAll){
			try{
				arrayFrom(context.querySelectorAll(selector));
                if(selector.indexOf('#') != -1 && typeof(elements[0]) != 'undefined'){
                    return elements[0];
                }
                else{
                    return elements;
                }
			} catch (e){}
		}

		currentDocument = context.ownerDocument || context;
		parse(selector.replace(reTrim, ''));
		find();

        if(selector.indexOf('#') != -1 && typeof(elements[0]) != 'undefined'){
            return elements[0];
        }
        else{
            return elements;
        }
   
	};

	var matchSelector = function(node){
		if (parsed.tag){
			var nodeName = node.nodeName.toUpperCase();
			if (parsed.tag == '*'){
				if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
			} else {
				if (nodeName != parsed.tag) return false;
			}
		}

		if (parsed.id && node.getAttribute('id') != parsed.id){
			return false;
		}

		if ((parsedClasses = parsed.classes)){
			var className = (' ' + node.className + ' ');
			for (var i = parsedClasses.length; i--;){
				if (className.indexOf(' ' + parsedClasses[i] + ' ') < 0) return false;
			}
		}

		if ((parsedPseudos = parsed.pseudos)){
			for (var i = parsedPseudos.length; i--;){
				var pseudoClass = pseudos[parsedPseudos[i]];
				if (!(pseudoClass && pseudoClass.call($u, node))) return false;
			}
		}

		return true;
	};

	var find = function(){

		var parsedId = parsed.id,
			merge = ((parsedId && parsed.tag || parsed.classes || parsed.pseudos)
				|| (!parsedId && (parsed.classes || parsed.pseudos))) ?
				arrayFilterAndMerge : arrayMerge;

		if (parsedId){

			var el = currentDocument.getElementById(parsedId);
			if (el && (currentDocument === context || contains(el))){
				merge([el]);
			}

		} else {

			merge(context.getElementsByTagName(parsed.tag || '*'));

		}

	};

	var parse = function(selector){
		parsed = {};
		while ((selector = selector.replace(/([#.:])?([^#.:]*)/, parser))){};
	};

	var parser = function(all, simbol, name){
		if (!simbol){
			parsed.tag = name.toUpperCase();
		} else if (simbol == '#'){
			parsed.id = name;
		} else if (simbol == '.'){
			if (parsed.classes){
				parsed.classes.push(name);
			} else {
				parsed.classes = [name];
			}
		} else if (simbol == ':'){
			if (parsed.pseudos){
				parsed.pseudos.push(name);
			} else {
				parsed.pseudos = [name];
			}
		}
		return '';
	};

	var slice = Array.prototype.slice;
	var arrayFrom = function(collection){
		elements = slice.call(collection, 0);
	};
	var arrayMerge = function(collection){
		for (var i = 0, node; node = collection[i++];){
			elements.push(node);
		}
	};
	try {
		slice.call(document.documentElement.childNodes, 0);
	} catch(e) {
		arrayFrom = arrayMerge;
	}

	var arrayFilterAndMerge = function(found){
		for (var i = 0, node; node = found[i++];){
			if (matchSelector(node)) elements.push(node);
		}
	};

	var contains = function(node){
		do {
			if (node === context) return true;
		} while ((node = node.parentNode));
		return false;
	};

	$u['pseudos'] = pseudos;
	$u['context'] = document;
	global['uSelector'] = $u;
	if (!global['$u']) global['$u'] = $u;

})(this, document);

(function(){

	var pseudos = {
		empty: function(node){
			var child = node.firstChild;
			return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
		},
		'first-child': function(node){
			while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
			return true;
		},
		'last-child': function(node){
			while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
			return true;
		},
		'only-child': function(node){
			return this.pseudos['first-child'](node) && this.pseudos['last-child'](node);
		},
		'first-of-type': function(node){
			var nodeName = node.nodeName;
			while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
			return true;
		},
		'last-of-type': function(node){
			var nodeName = node.nodeName;
			while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
			return true;
		},
		'only-of-type': function(node){
			return this.pseudos['first-of-type'](node) && this.pseudos['last-of-type'](node);
		},
		disabled: function(node){
			return node.disabled;
		},
		checked: function(node){
			return node.checked || node.selected;
		},
		selected: function(node){
			return node.selected;
		}
	};

	for (var pseudo in pseudos){
		$u.pseudos[pseudo] = pseudos[pseudo];
	}

})();

Rhy.select = $u;
$rh = $u;

